/*************************************************************************
 Crytek Source File.
 Copyright (C), Crytek Studios, 2001-2004.
 -------------------------------------------------------------------------
 $Id$
 $DateTime$
 Description:  the part of a defence wall that every connection needs
 -------------------------------------------------------------------------
 History:
 - 10/08/2004   : Created by Craig Tiller, based on CDefenceWall by Timur
*************************************************************************/

#ifndef __DEFENCECONTEXT_H__
#define __DEFENCECONTEXT_H__

#pragma once

#include "Config.h"
#include "Network.h"

#if USE_DEFENCE

#include "TimeValue.h"
#include "IDataProbe.h"
#include "DefenceData.h"

#include "NetTimer.h"
#include "STLMementoAllocator.h"

class CNetChannel;

struct SCheckContext
{
	string filename;
};

class CProbeRequest
{
public:
	virtual ~CProbeRequest(){}
	CProbeRequest( const SDataProbeContext& c, bool sv, volatile int * pRefCount ) : ctx(c), ncodes(0), isServer(sv), m_pRefCount(pRefCount) 
	{
		SCOPED_GLOBAL_LOCK;
		CryInterlockedIncrement(m_pRefCount);
	}
	SDataProbeContext ctx;
	// maximum probes done by the probe thread
	static const int NUM_CODES = 2;
	uint64 codes[NUM_CODES];
	int ncodes;
	bool isServer;

	virtual void Complete() = 0;
	virtual void Abort() {};

protected:
	// must be instantiated at the start of Complete()
	class CReleaseRefCount
	{
	public:
		CReleaseRefCount( CProbeRequest * pReq )
		{
			m_pRefCount = pReq->m_pRefCount;
		}

		~CReleaseRefCount()
		{
			CryInterlockedDecrement(m_pRefCount);
		}

	private:
		volatile int * m_pRefCount;
	};

private:
	// pointer to an owner refcount - for implementing IDefenceContext::CanRemove
	volatile int * m_pRefCount;
};

class CProbeThread : public CrySimpleThread<>
{
public:
	CProbeThread() {}

	void Run();
	void Cancel() { Push(NULL); }

	void Push(CProbeRequest*);
	void AbortPendingRequests(void);

private:
	typedef CryMutex TLock;
	typedef CryConditionVariable TCond;
	TLock m_mtx;
	TCond m_cond;
	std::queue<CProbeRequest*> m_incoming;
};

class CClientDefence : public CNetMessageSinkHelper<CClientDefence, IDefenceContext>
{
public:
	CClientDefence( CNetChannel * );
	void DefineProtocol( IProtocolBuilder * pBuilder );
	virtual void AddProtectedFile( const string&, bool headerOnly );
	virtual void ClearProtectedFiles();
	virtual bool HasRemoteDef( const SNetMessageDef * pDef );
	virtual bool CanRemove() { return m_pendingRequests == 0; }
  virtual void StartCvarRequests(){}
  virtual void EndCvarRequests(){}
	virtual void Abort() {}

	NET_DECLARE_IMMEDIATE_MESSAGE(CheckMessageFile0);
  NET_DECLARE_IMMEDIATE_MESSAGE(CheckMessageFile1);
  NET_DECLARE_IMMEDIATE_MESSAGE(CheckMessageFile2);
  NET_DECLARE_IMMEDIATE_MESSAGE(CheckMessageFile3);
  NET_DECLARE_IMMEDIATE_MESSAGE(CheckMessageFile4);
  NET_DECLARE_IMMEDIATE_MESSAGE(CheckMessageCvar);

private:
	CNetChannel * m_pParent;
	CMementoMemoryManagerPtr m_pMMM;
	volatile int m_pendingRequests;

	class CClientReplyMessage;
	class CClientQuery;
};

class CServerDefence : public CNetMessageSinkHelper<CServerDefence, IDefenceContext>
{
public:
	CServerDefence( CNetChannel * );
	~CServerDefence();
	void DefineProtocol( IProtocolBuilder * pBuilder );
	virtual void AddProtectedFile( const string&, bool headerOnly );
	virtual void ClearProtectedFiles();
	virtual bool HasRemoteDef( const SNetMessageDef * pDef );
	virtual bool CanRemove() { return m_pendingRequests == 0; }
  virtual void StartCvarRequests();
  virtual void EndCvarRequests();
	virtual void Abort();

	NET_DECLARE_IMMEDIATE_MESSAGE(CheckMessage);

private:
	void SendNextRequest();
  void SendNextRequestFile();
  void SendNextRequestCVar();
	void SentRequest( uint32 id, const SDataProbeContext& ctx );

	NetTimerId m_timer;
	NetTimerId m_nextSend;
	static void TimerCallback(NetTimerId, void* p, CTimeValue now);
	static void SendNextRequestCallback(NetTimerId, void* p, CTimeValue now);

	CNetChannel * m_pParent;
	CMementoMemoryManagerPtr m_pMMM;

	struct SDefenseFileInfo
	{
		string m_fileName;
		bool	 m_headerOnly;
	};
	typedef std::multimap<uint32, SDefenseFileInfo, std::less<uint32>, STLMementoAllocator< std::pair<uint32, SDefenseFileInfo> > > TPFMap;
  typedef std::auto_ptr<TPFMap> PTPFMap;
	struct ProtectedList
  {
    PTPFMap m_protectedFiles;
  };
  std::vector<ProtectedList*> m_protectedLists;

	struct SPending
	{
		SPending( const SDataProbeContext& c ) : ctx(c), when(g_time) {}
		SDataProbeContext ctx;
		CTimeValue when;
	};
	typedef std::map<uint32, SPending, std::less<uint32>, STLMementoAllocator< std::pair<uint32, SPending> > > TPMap;
	std::auto_ptr<TPMap> m_pending;
	uint32 m_nextID;

  class CServerCheckMessage;
	class CServerCheckMessageFile;
  class CServerCheckMessageCvar;
	class CServerValidate;

	bool m_incDefenceCount;
  bool m_cvarCheckEnabled;
	volatile int m_pendingRequests;
};

#endif //USE_DEFENCE

#endif
